package searchprocessing.exactmatch;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;



import database.Jdbcmysql;
import database.LuceneSearch;
import searchprocessing.exactmatch.MatchedUnit;
import structure.AssetNode;
import structure.EdgeMatchpair;
import structure.NodeMatchpair;
import structure.Relationship;

public class ExactMatchUnit extends MatchedUnit{
	//private boolean startExactMatch = true ;
	private LuceneSearch search = new LuceneSearch();
	private HashMap<String,HashMap<String,Double>> resultfromLucene = new HashMap<String,HashMap<String,Double>>();
	
	/**
	 * 設定Query的點跟邊，即時建立字典，正規化Query圖
	 */
	public ExactMatchUnit(HashMap<String, AssetNode> node,ArrayList<Relationship> edge) {
		edge = direction_normalize(edge);
		setNode(node);
		setEdge(edge);
	}
	
	/**
	 * 取得Lucene的查詢名稱相似
	 * @return LuceneSearch
	 */
	public LuceneSearch getLuceneSearch(){
		return this.search;
	}
	
	/**
	 * 利用Lucene取得查詢之相似候選集
	 * @return HashMap<String,Double>
	 */
	public HashMap<String,Double> getCandidate(String query){
		return getLuceneSearch().searchSimNode(query);
	}
	
	/**
	 * 檢查有無Map中資料，有資料回傳，無資料則查詢後回傳
	 * @param idVersion
	 * @return HashMap<String, Double>
	 */
	private HashMap<String, Double> getExistResult(String idVersion,String queryWord){
		//若存在回傳即可
		if(resultfromLucene.containsKey(idVersion)){
			return resultfromLucene.get(idVersion);
		}
		else{
		//若不存在搜尋後，並放入resultfromLucene裡面
			HashMap<String,Double> asset_candidate = new HashMap<String,Double>();
			asset_candidate = getCandidate(queryWord);
			putAssetResultFromLucene(idVersion,asset_candidate);
			return asset_candidate;
		}
		
	}
	
	/**
	 * 為了避免重複查詢Lucene，故將結果存進內存記憶體HashMap
	 * @param idVersion
	 * @param result
	 */
	private void putAssetResultFromLucene(String idVersion , HashMap<String,Double> result){
		if(resultfromLucene.containsKey(idVersion)){
			this.resultfromLucene.put(idVersion, result);
		}
	}
	
	/**
	 * 檢查有無資料，若有則回傳，無則Null
	 * @param idVersion
	 * @return  HashMap<String, Double>
	 */
	public HashMap<String, Double> getAssetResultFromLucene(String idVersion){
		if(resultfromLucene.containsKey(idVersion)){
			return this.resultfromLucene.get(idVersion) ;
		}
		return null;
	}
	
	/**
	 * 找出交集的Asset，降低資料筆數
	 * @param keySet
	 * @param existResult
	 * @return HashMap<String, Double>
	 */
	private HashMap<String, Double> extractData(Set<String> keySet,
			HashMap<String, Double> existResult) {
		//回傳出交集的部分
		HashMap<String,Double> Intersection = new  HashMap<String,Double>();
		
		for(String idVersion : keySet){
			if(existResult.containsKey(idVersion)){
				Intersection.put(idVersion,existResult.get(idVersion));
			}
		}	
		return Intersection;
	}
	
	/**
	 * 開始比對
	 */
	public void matched() throws IOException{
		Jdbcmysql db = new Jdbcmysql();
		String[][] solutiontable = db.catchSolution();
		
		if(solutiontable!=null){
			for(int num=0 ; num < solutiontable.length ; num++){
				
				HashMap<String, AssetNode> gdbNode = new HashMap<String, AssetNode>();
				ArrayList<Relationship> gdbEdge = new ArrayList<Relationship>();
				String[] solutionid = solutiontable[num][0].split("_");
				//從SQL載入當前比較的智產圖
				gdbNode = db.searchNode(solutionid[0]);	
				gdbEdge = db.searchEdge(solutionid[0]);
				//更新遺失資訊及正規化智產圖
				gdbEdge = updateEdge(gdbNode,gdbEdge);
				gdbEdge = direction_normalize(gdbEdge);
				//getData_HeadMap(gdbEdge);
				
				/*if(num==9){
					System.out.printf("\n"+num);
				}*/
				
				
				/**
				 * 比對部分checkMatch
				 */
				if(checkMatch(gdbNode,gdbEdge,solutiontable[num][0])){
					double score ;
					if(gdbNode.size()>1 && gdbEdge.size()>=1){
						score = calculateAllScore(edgeMatch);
						if(score >=0.0){
							solution_EdgeMatch.put(solutiontable[num][0], edgeMatch);
							solution_NodeMatch.put(solutiontable[num][0], nodeMatch);
							queryMatch.put(solutiontable[num][0], score);
							convertName.put(solutiontable[num][0], solutiontable[num][1]);
							System.out.println("在"+solutiontable[num][1]+"全部找到　整體分數"+score);
						}
					}
					else if(gdbNode.size()==1){
						for(NodeMatchpair nodeScore : nodeMatch)
							System.out.println(nodeScore.getDataNode().getAssetName()+" from "
									+nodeScore.getDataNode().getGrpahID()+" : "+nodeScore.getSimilarNodeScore());
					}
				}
			}
		}
		System.out.println();
		mapSort(queryMatch);
	}

	/**
	 * 比對程序(邊pair,點pair)
	 * @param gdbNode
	 * @param gdbEdge
	 * @param string
	 * @return boolean
	 */
	public boolean checkMatch(HashMap<String, AssetNode> gdbNode,ArrayList<Relationship> gdbEdge, String string) {
		
		edgeMatch = new ArrayList<EdgeMatchpair>() ;
		nodeMatch = new ArrayList<NodeMatchpair>() ;
		setEdgeMatchType(0);
		
		//有邊有點的例子(Graph查詢)
		ArrayList<Relationship> queryCheck = new ArrayList<Relationship>();
		
		if(graph_Edge.size() > 0 && graph_Node.size() > 1){
			ArrayList<Relationship> loadDataGraph = (ArrayList<Relationship>) gdbEdge.clone();
			
			for(int i = 0 ; i < graph_Edge.size() ; i++){
				//初始邊的flag及取得邊
				Relationship queryEdge = new Relationship();
				queryEdge = graph_Edge.get(i) ;
				queryEdge.isEmatched = false ;
				

				//檢查是否是虛擬邊
				if((queryEdge.getHead().getAssetLayerType()!= 0) 
						&& (queryEdge.getTail().getAssetLayerType()!= 0)){
					
					HashMap<String,Double> head_candidate = new HashMap<String,Double>();
					HashMap<String,Double> tail_candidate = new HashMap<String,Double>();
					
					String hIdVersion = queryEdge.getHead().getAssetID()+"_"+queryEdge.getHead().getVersion();
					String hAssetName = queryEdge.getHead().getAssetName();
					String tIdVersion = queryEdge.getTail().getAssetID()+"_"+queryEdge.getTail().getVersion();
					String tAssetName = queryEdge.getTail().getAssetName();
					
					//以Node.name來找
					//為了避免重複查詢Lucene，故將結果存進內存記憶體HashMap
					//因為候選集的資料很多，必須先擷取出跟當前智產圖相關之智產
					head_candidate = extractData(gdbNode.keySet(),getExistResult(hIdVersion,hAssetName));
					tail_candidate = extractData(gdbNode.keySet(),getExistResult(tIdVersion,tAssetName));
					

					if(head_candidate.size() > 0 && tail_candidate.size() > 0){
						
						if(!queryEdge.getRelations().equals("*")){
							//head 跟 tail 都有候選集，看是否存在data邊
							ArrayList<Relationship> edge_candidate = new ArrayList<Relationship>();
							HashMap<Relationship , Double> edgeshipScore = new HashMap<Relationship , Double>();
							EdgeMatchpair matchedEdge = new EdgeMatchpair();
							NodeMatchpair matchedNode = new NodeMatchpair();
							ArrayList<EdgeMatchpair> querytodata = new ArrayList<EdgeMatchpair>();
							
							//test
							//System.out.println("目前的點查詢"+hAssetName+","+tAssetName);
							
							for(Map.Entry<String, Double> headnode : head_candidate.entrySet()){
								for(Map.Entry<String, Double> tailnode : tail_candidate.entrySet()){
									boolean flag = false;//因可能因正規化而匹配錯誤
									if(head_candidate.containsKey(tailnode.getKey()) && 
											tail_candidate.containsKey(headnode.getKey())){
										flag = true ;
									}
									//檢查候選集的組合，是否存在於data Edge(回傳一條)

									edge_candidate.addAll(checkEdge(headnode,tailnode,loadDataGraph,gdbNode,flag));

								}
							}
							
							//一條Query邊找出很多候選邊
							if(!edge_candidate.isEmpty()){
								//完全比對不允許邊模糊若<1即去掉
								edgeshipScore = equalship(edge_candidate,queryEdge.getRelations());
								
								if(!edgeshipScore.isEmpty()){
									matchedEdge = selectScoreMaxEdge(edgeshipScore,head_candidate,tail_candidate);
									if(matchedEdge!=null){
										matchedEdge.setQueryEdge(queryEdge);
										querytodata.add(matchedEdge);
									}
									else{
										return false;
									}
								}
							}
							
							//如果還有候選集
							if(querytodata.size() > 0){
								matchedEdge = maxScoreEdgeMatched(querytodata);
								if(matchedEdge!=null){
									queryEdge.isEmatched = true ;
									
									//防止重複匹配，將已配的邊去掉
									loadDataGraph = rmdbedge(loadDataGraph,matchedEdge) ;
									
									edgeMatch.add(matchedEdge);
									//頭點匹配
									matchedNode = new NodeMatchpair();
									matchedNode.setQueryNode(matchedEdge.getQueryEdge().getHead());
									matchedNode.setDataNode(matchedEdge.getDataEdge().getHead());
									matchedNode.setSimilarNodeScore(head_candidate.get(
											matchedEdge.getDataEdge().getHead().getAssetID()+"_"+
											matchedEdge.getDataEdge().getHead().getVersion()));
									nodeMatch.add(matchedNode);
									
									matchedNode = new NodeMatchpair();
									//尾點匹配
									matchedNode.setQueryNode(matchedEdge.getQueryEdge().getTail());
									matchedNode.setDataNode(matchedEdge.getDataEdge().getTail());
									matchedNode.setSimilarNodeScore(tail_candidate.get(
											matchedEdge.getDataEdge().getTail().getAssetID()+"_"+
											matchedEdge.getDataEdge().getTail().getVersion()));
									nodeMatch.add(matchedNode);
									
									queryCheck.add(queryEdge);
								}
								else{
									return false;
								}
								
							}
							else{
								return false;
							}
						}
						else{ // relationship type is "*"
							
						}
					}
					else{ //有個點沒有候選集表示找不到該點
						return false;
					}
				}
				else{
					queryEdge.isEmatched = true ;
					queryCheck.add(queryEdge) ;
				}
			}
			//check isEqual_EdgeNumber
			if(queryCheck.size() == graph_Edge.size()){				
				return true ;

			}
			else {
				return false ;
			}
		}
		return false;
	}

	/**
	 * 更新邊的資訊
	 * @param gdbNode
	 * @param gdbEdge
	 * @return ArrayList<Relationship>
	 */
	private ArrayList<Relationship> updateEdge(HashMap<String, AssetNode> gdbNode, ArrayList<Relationship> gdbEdge) {
		ArrayList<Relationship> dbEdge = new ArrayList<Relationship>();
		for(Relationship edge : gdbEdge){
			edge.setHead(gdbNode.get(edge.getHead().getAssetID()+"_"+edge.getHead().getVersion())) ;
			edge.setTail(gdbNode.get(edge.getTail().getAssetID()+"_"+edge.getTail().getVersion())) ;
			dbEdge.add(edge);
		}
		return dbEdge;
	}
	
	/**
	 * 檢查邊上的關係容許完全，模糊，不考慮 
	 * @param edge_candidate
	 * @param relations
	 * @return HashMap<Relationship , Double>
	 */
	protected HashMap<Relationship , Double> equalship(ArrayList<Relationship> edge_candidate, String relations) {
		HashMap<Relationship , Double> mayCandidate = new HashMap<Relationship , Double>();
		
		for(Relationship checkship : edge_candidate){
			try{
				if(exactship(checkship.getRelations(),relations)){
					mayCandidate.put(checkship , 1.0);
				}
			}
			catch(Exception e){
				System.out.println(e);
			}
		}
		return mayCandidate ;
	}
}
